#include <engine.h>

RectangleLink::~RectangleLink()
{
    // only the header's destructor release all resources
    //
    if (!RectangleValid(&m_rect)) {
        assert(m_free);

#ifdef _DEBUG
        Int32 count = 0;
#endif
        RectangleLink* node = (RectangleLink*)Next();
        while (node != this) {
#ifdef _DEBUG
            if (!node->m_free) {
                count++;
            }
#endif
            node->Detach();
            delete node;
            node = (RectangleLink*)Next();
        }

        assert(count == m_rect.x);

        m_rect.x = 0;
    }
}

ECode RectangleLink::Add(
    /* [in] */ Rectangle* rectangle)
{
    ECode ec = NOERROR;

    assert(rectangle && RectangleValid(rectangle));

    Rectangle rect = *rectangle;

    g_lock.Lock();

    assert(!RectangleValid(&m_rect) && m_free);

    RectangleLink* node = NULL;
    RectangleLink* freeNode = NULL;

    node = (RectangleLink *)Next();
    while (node != this) {
        if (!node->m_free && RectangleIntersects(&rect, &node->m_rect)) {
            RectangleUnion(&node->m_rect, &rect);

            goto Exit;
        }

        node = (RectangleLink *)node->Next();
    }

    assert(node == this);
    node = (RectangleLink *)Next();
    while (node != this) {
        if (node->m_free) {
            freeNode = node;
            break;
        }

        node = (RectangleLink *)node->Next();
    }

    if (!freeNode) {
        freeNode = (RectangleLink *)new RectangleLink;
        if (!freeNode) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }

        InsertFirst(freeNode);
    }

    freeNode->m_free = FALSE;
    freeNode->m_rect = rect;

    m_rect.x++;
    assert(m_rect.x > 0);

Exit:
    g_lock.Unlock();

    return ec;
}

ECode RectangleLink::PreciseAdd(
    /* [in] */ Region* region)
{
    assert(region && RegionRegular(region));

    Rectangle rect = RECTANGLE_INIT_FROM_REGION(region);

    return PreciseAdd(&rect);
}

ECode RectangleLink::PreciseAdd(
    /* [in] */ Rectangle* rectangle)
{
    assert(rectangle && RectangleValid(rectangle));

    ECode ec = NOERROR;
    Rectangle rect = *rectangle;

    g_lock.Lock();

    // assure "this" is the header;
    //
    assert(!RectangleValid(&m_rect) && m_free);

    RectangleLink* node = NULL;
    RectangleLink* freeNode = NULL;

    ForEachDLinkNode(RectangleLink*, node, this) {
        if (!node->m_free) {
            if (RectanglePrecisionUnion(&rect, &node->m_rect, &rect)) {
                node->m_free = TRUE;
                m_rect.x--;
                assert(m_rect.x >= 0);

                ec = PreciseAdd(&rect);
#ifdef _DEBUG
                if (GFX_FAILED(ec)) {
                    goto Exit;
                }
#endif

                goto Exit;
            }
            else if (RectangleIntersects(&rect, &node->m_rect)) {
                Rectangle rectangleRemain[4];
                Int32 count;

                count = RectangleSubRectangle(
                                rect, node->m_rect, rectangleRemain);
                assert(count <= 4);
                for (int i = 0; i < count; i++) {
                    ec = PreciseAdd(&rectangleRemain[i]);
                    if (GFX_FAILED(ec)) {
                        goto Exit;
                    }
                }

                goto Exit;
            }
        }
    }

    assert(node == this);

    ForEachDLinkNode(RectangleLink*, node, this) {
        if (node->m_free) {
            freeNode = node;
            break;
        }
    }

    if (!freeNode) {
        freeNode = (RectangleLink *)new RectangleLink;
        if (!freeNode) {
            GFX_ERROR_MESSAGE("Not enough memory\n");
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }

        InsertFirst(freeNode);
    }

    freeNode->m_free = FALSE;
    freeNode->m_rect = rect;

    m_rect.x++;
    assert(m_rect.x > 0);

Exit:
    g_lock.Unlock();

    return ec;
}

ECode RectangleLink::PreciseRemove(
        /* [in] */ Region* region)
{
    assert(region && RegionRegular(region));

    Rectangle rectangle = RECTANGLE_INIT_FROM_REGION(region);

    return PreciseRemove(&rectangle);
}

ECode RectangleLink::PreciseRemove(
    /* [in] */ Rectangle* rectangle)
{
    assert(rectangle && RectangleValid(rectangle));
    Rectangle rect = *rectangle;

    ECode ec = NOERROR;

    g_lock.Lock();

    // assure "this" is the header;
    //
    assert(!RectangleValid(&m_rect) && m_free);

    RectangleLink* node = NULL;

    ForEachDLinkNode(RectangleLink*, node, this) {
        if (!node->m_free) {
            if (RectangleEqual(&rect, &node->m_rect)) {
                node->m_free = TRUE;
                m_rect.x--;
                assert(m_rect.x >= 0);

                goto Exit;
            }
            else if (RectangleIntersects(&rect, &node->m_rect)) {
                node->m_free = TRUE;
                m_rect.x--;
                assert(m_rect.x >= 0);

                Rectangle rectangleRemain[4];
                Int32 count;

                count = RectangleSubRectangle(
                                node->m_rect, rect, rectangleRemain);
                assert(count <= 4);
                for (int i = 0; i < count; i++) {
                    ec = PreciseAdd(&rectangleRemain[i]);
                    if (GFX_FAILED(ec)) {
                        goto Exit;
                    }
                }
            }
        }
    }

Exit:
    g_lock.Unlock();

    return ec;
}

Void RectangleLink::RemoveAll()
{
    assert(!RectangleValid(&m_rect) && m_free);
    g_lock.Lock();

    RectangleLink* node;
    ForEachDLinkNode(RectangleLink*, node, this) {
        node->m_free = TRUE;
    }

    m_rect.x = 0;

    g_lock.Unlock();
}

ECode RectangleLink::Clone(
    /* [in] */ RectangleLink* header)
{
    assert(header && !RectangleValid(&header->m_rect) && header->m_free);
    assert(!RectangleValid(&m_rect) && m_free);

    ECode ec = NOERROR;
#ifdef _DEBUG
    Int32 count = 0;
#endif

    g_lock.Lock();

    RemoveAll();

    RectangleLink* node;

    ForEachDLinkNode(RectangleLink*, node, header) {
        if (!node->m_free) {
            ec = Add(&node->m_rect);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }

#ifdef _DEBUG
            count++;
#endif
        }
    }

    assert(count == header->m_rect.x);
    assert(m_rect.x == header->m_rect.x);

Exit:
    g_lock.Unlock();

    return ec;
}

Boolean RectangleLink::Equal(
    /* [in] */ RectangleLink* header)
{
    assert(header && !RectangleValid(&header->m_rect) && header->m_free);
    assert(!RectangleValid(&m_rect) && m_free);

    if (m_rect.x != header->m_rect.x) {
        return FALSE;
    }

    RectangleLink* node;
    RectangleLink* compare = header;

    ForEachDLinkNode(RectangleLink*, node, this) {
        if (!node->m_free) {
            do {
                compare = (RectangleLink*)compare->Next();
            } while (compare != header && compare->m_free);

            if (compare == header
                    || !RectangleEqual(&node->m_rect, &compare->m_rect)) {
                return FALSE;
            }
        }
    }

    return TRUE;
}

